/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief Describes the MaCICtrl protocol types.
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 * $Id: MaCICtrlTypes.hpp,v 1.19 2009-05-13 07:18:17 amaula Exp $
 *
 */
#ifndef _MACI_MACICTRLTYPES_HPP_
#define _MACI_MACICTRLTYPES_HPP_
#include <inttypes.h>
#include <string.h>
#include <string>
#include "ownutils.h"
#include "owndebug.h"
#include "common/text.hpp"

namespace MaCI {

  /** MaCI - MaCICtrl interface.
   *
   * This namespace contains all the methods and types related
   * to the MaCICtrl protocol.
   */
  namespace MaCICtrl {
    
    /** Enumeration types for MaCICtrl data elements.
     */
    enum EMaCICtrlType { 
      KTypeUnknown               = -1, ///< Unkown type
      KTypeTimestamp             = 1,  ///< Common::TTimestamp of command
      KTypeCommand               = 2,  ///< TCommand entry
      KTypeGroupID               = 3,  ///< TGroup information structure
      KTypeService               = 4,  ///< TService information entry
      KTypeAuthentication        = 5,  ///< TAuthentication entry
      KTypeAccess                = 6,  ///< TAccess entry
      KTypeServiceIdentification = 7,  ///< TServiceIdentification entry.
      KTypeDeviceGroup           = 8   ///< TDeviceSubgroup entry.
    };
    
    
    /** Enumerations for MaCICtrl Command.
     */
    enum EMaCICtrlCommand { 
      KCommandGetInformation    = 1,   ///< Request information; Replied with all available Info headers.
      KCommandGetAccess         = 2,   ///< Request Access information
      KCommandSetAuthentication = 10,  ///< Set authentication information. (Set operation)
      KCommandTellInformation   = 20,  ///< "Tell" Information.
      KCommandTellAccess        = 21,  ///< "Tell" Access information.
      KCommandKill              = 30   ///< Secret weapon.
    };


    /** Device subgroup.
     * 
     * This is a field which supersedes the previously used
     * 'TGroupID'. 
     *
     * This container takes advantage of the Common::TText container,
     * which is used as a dynamic text container. The dynamic string
     * stored in the container is used as-is as a single group element
     * of a device.
     *
     * \note A container may not contain BOTH - a TGroupID container
     * and a non-zero number of TDeviceGroup containers.
     */
    typedef Common::TText TDeviceGroup;


    /** MaCI group identification information.
     *
     * This structure contains information for identifying a MaCI
     * service instance.  It contains the name and numerical ID.
     *
     * \deprecated This structure should not be used in new
     * applications, hence it has been replaced with the new
     * 'TDeviceGroup' defitinion which allows more flexible
     * grouping expressions.
     * 
     * \note A container may not contain BOTH - a TGroupID container
     * and a non-zero number of TDeviceGroup container.
     */
    struct TGroupID 
    {
      TGroupID(unsigned int aGID = 0, const char *aGroupname = "")
	: name(),
          id(aGID)
      {
        // Copy the provided name
        strncpy(this->name, aGroupname, sizeof(this->name)); 
        name[sizeof(this->name)-1] = 0; 

      }
      /** Print contents.
       */
      void Print(const unsigned int l) const {
        dPrint(l,"TGroupID (%p) contents:\n"
               "\tname = '%s'\n"
               "\tid = '%u'",
               this, name, id);
      }

      char name[64];       ///< Group name
      uint32_t id;         ///< GroupID in numeral form
    };



    /** Enumeration type for TService::type field.
     */
    enum EServiceType {
      /// Unknown/unset service type.
      KServiceTypeUnknown  = 0,

      /// GIMI Provided service in MaCI format.
      KServiceTypeProvided = 1, 
      
      /// GIMI Accepted service in MaCI format.
      KServiceTypeAccepted = 2,

      /// Unfortunate fact that GIMI uses this for Accepted. Defined
      /// here to avoid problems with accidentaly mixing GIMI and MaCI
      /// servicetypes. When this value is encountered, warnings are
      /// printed.
      KServiceTypeAcceptedToo = 3, 

      /// GIMI Accepted or Provided service. This must not be sent on
      /// a container. Used only for filtering / selecting services.
      KServiceTypeAny      = 4 
    };


    /** Enumeration type for TService::flags field.
     */
    enum EServiceFlag {
      KServiceFlagActive = 1 ///< Service is active.
    };


    /** MaCI group member service entry.
     *
     * This structure contains fields to speficy a service major/minor
     * which is part of the current MaCI group. These elements can be
     * added multiple times for multiple members. All TService
     * elements in the container must be stored in sequence.
     */
    struct TService
    {
      TService(const uint32_t aMajor = 0, 
               const uint32_t aMinor = 0,
               const uint16_t aType = 0,
               const uint16_t aFlags = 0)
        : servicemajor(aMajor),
          serviceminor(aMinor),
          type(aType),
          flags(aFlags) {}
      

      /** Print contents.
       */
      void Print(const unsigned int l) const {
        dPrint(l,"TService (%p) contents:\n"
               "\tservicemajor = %u\n"
               "\tserviceminor = %u\n"
               "\ttype         = %u\n"
               "\tflags        = 0x%04x",
               this,
               servicemajor,
               serviceminor,
               type,
               flags);
      }
      
      /** Compare operator for std::map
       */
      bool operator < (const TService &aSrc) const {
        bool result = false;

        if (servicemajor < aSrc.servicemajor) result = true;
        else if (servicemajor == aSrc.servicemajor) {
          if (serviceminor < aSrc.serviceminor) result = true;
          else if (serviceminor == aSrc.serviceminor) {
            if (type < aSrc.type) result = true;
          }
        }

        return result;
      }
 
      /** Test for equality. Flags are not compared. Test is
       * absolute, no wildcards are considered.
       */
      bool operator == (const TService &aSrc) const {
        return (servicemajor == aSrc.servicemajor &&
                serviceminor == aSrc.serviceminor &&
                type == aSrc.type);
      }
      

      /** Test services for a match. 'Wildcards' are considered.
       *
       * Match is considered to be true when either one has 'Unset'
       * value on a field.
       *
       * @return 'true' if entries are considered to be matching.
       */
      bool Match(const TService &aSrc) const {
        bool r = true;
        if (servicemajor != 0 && aSrc.servicemajor != 0 && servicemajor != aSrc.servicemajor) r = false;
        if (r && serviceminor != 0 && aSrc.serviceminor != 0 && serviceminor != aSrc.serviceminor) r = false;
        if (r && type != 0 && aSrc.type != 0 && type != aSrc.type) r = false;
        return r;
      }


      uint32_t servicemajor;          ///< GIMI Major id of service
      uint32_t serviceminor;          ///< GIMI Minor id of service
      uint16_t type;                  ///< Service type. See MaCI::MaCICtrl::EServiceType.
      uint16_t flags;                 ///< Service flags. See MaCI::MaCICtrl::EServiceFlag.
    };

    
    /** Structure containing service identification information.
     *
     * This information is used by Client to be able to bind strings
     * like 'J2B2.Image.LeftCamera' to correct service.
     *
     */
    struct TServiceIdentification {
      TServiceIdentification(const std::string &aInterfaceInstanceName = "")  
        : interfaceinstancename() {
        
        // Store InterfaceInstance name.
        strncpy(interfaceinstancename, aInterfaceInstanceName.c_str(), sizeof(interfaceinstancename));
        interfaceinstancename[sizeof(interfaceinstancename)-1] = 0;
      }
      

      /** Print contents.
       */
      void Print(const unsigned int l) const {
        dPrint(l,"TServiceIdentification (%p) contents:\n"
               "\tinterfaceinstancename = '%s'",
               this,
               interfaceinstancename);
      }
      
      /** Test names for a match. 'Wildcards' are considered.
       *
       * Match is considered to be true when either one has 'Unset'
       * value on a field.
       *
       * @return 'true' if entries are considered to be matching.
       */
      bool Match(const TServiceIdentification &aSrc) {
        bool r = true;
        if (interfaceinstancename[0] && 
            aSrc.interfaceinstancename[0] && 
            strcmp(interfaceinstancename, aSrc.interfaceinstancename) != 0) r = false;
        return r;
      }

      char interfaceinstancename[32]; ///< Interface Instance name; like 'LaserSICK' or 'Bumpers'.
    };


    /** MaCICtrl command.
     *
     * This structure contains command field to be used when sending
     * action requests to MaCICtrl enabled clients.
     *
     * \see MaCI::MaCICtrl::EMaCICtrlCommand
     */
    struct TCommand 
    {
      TCommand(unsigned int aCmd = 0) 
	: cmd(aCmd) {}
      uint32_t cmd;           ///< Command
    };


    /** Flags for Authentication request.
     */
    enum EAuthenticationFlags {
      /** Request Explicit access to the interface.
       * 
       * If this flag is set; the interface attempts to request
       * explicit access. Explicit access may or may not be granted
       * depending on the implementation of the module using the
       * interface OR whether the explicit mode is currently acquired
       * by somebody else. 
       */
      KAuthenticationFlagRequestAccess = (1<<0),
      
      
      /** Release Explicit access to the interface.
       * 
       * This flag releases explicit access to interface. It cannot be
       * set simultaneously with flag:
       * 'KAuthenticationFlagRequestExplicitAccess'
       */
      KAuthenticationFlagReleaseAccess = (1<<1)
    }; 


    /** Authentication information container.
     *
     */
    struct TAuthentication
    {
      /** TAuthentication constructor.
       * 
       * Constructor can take all the keys and their lengths and copy
       * them to container. Default values are set so that keys
       * are initialized to all-zeroes.
       * 
       * \note The keys are compared using memcmp() NOT strcmp(), so
       * ALL the bytes in the key are meaningful. Default constructor
       * first zeroes all bytes, THEN copies the user provided bytes
       * on the structure.
       * 
       */
      TAuthentication(const unsigned char *aKeyAccess = NULL, const unsigned int aKeyAccessLen = 0, 
                      const unsigned int aFlags = 0)
        : keyAccess(),
          flags(aFlags) {
        // Zero the whole structure.
        memset(this, 0x00, sizeof(TAuthentication));
        
        // Set the keys (if they are non-zero in length)
        if (aKeyAccessLen) SetAccessKey(aKeyAccess, aKeyAccessLen);
      }


      /** Set Access key.
       * @param[in] aKey        Pointer to beginning of key data.
       * @param[in] aLen        Length of data to use.
       */
      void SetAccessKey(const unsigned char *aKey, const unsigned int aLen) {
        memcpy(keyAccess, aKey, MIN(aLen, sizeof(keyAccess)));
      }


      /** Set Flags.
       *
       * \param[in] aFlags      Flags to set.
       */
      void SetFlags(const unsigned int aFlags) {
        flags = aFlags;
      }


      /** Compare two authentication structures.
       *
       * Compare function for authentication structures. Returns true if they
       * are equal.
       * 
       * @param[in] ref         Reference to structure to compare to
       * @return                'true' if the structures are equal,
       *                        otherwise 'false'.
       */
      bool operator==(const TAuthentication &ref) const {
        return memcmp(this->keyAccess, ref.keyAccess, sizeof(keyAccess)) == 0;
      }


      /** Check whether the given key matches the Access key.
       *
       * \note The function assumes that the provided pointer
       * contains enough bytes for comparison (16 bytes)
       * 
       * @param[in] aKey        Pointer to key to check against.
       * @return                'true' when parameter and key match,
       *                        'false' on mismatch.
       */
      bool CheckKeyAccess(const unsigned char *aKey) {
        return !memcmp(keyAccess, aKey, sizeof(keyAccess));
      }
      
      uint8_t keyAccess[16]; ///< Key to control any access (Must be correct to use rest)
      uint32_t flags; ///< Flags for specifying extra parameters.
    };


    /** Access bits for the TAccess access information container.
     *
     * This enumeration containes the values for transferring access
     * information. If a GetAccess results in zero access, the client
     * is not allowed to access the device in question. The resulting
     * access mask is a binary combination of these values.
     */
    enum EAccess { 
      /** Any Access.
       *
       * Does client have any access at all? If this is NOT set, the
       * client doesn't have ANY permissions to the interface without
       * additional authentication.
       */ 
      KAccessNormal   = (1<<0),
    };
    

    /** Access information container.
     * 
     * This structure carries access bits for this interface.
     *
     * \see MaCI::MaCICtrl::EAccess
     */
    struct TAccess
    {
      /** Default constructor. 
       * Without parameters access is set to 'All Denied'.
       */
      TAccess(const uint32_t aAcc = 0)
        : access(aAcc),
          value(0) {}
      

      /** Check whether the access structure contains the Access bit.
       *
       * @return                'true' if 'Access' is set, otherwise
       *                        'false'.
       */
      bool IsAccess(void) const { return (access&KAccessNormal); }


      /** Sets the access mask to given value.
       *
       * \note The function doesn't check whether the bits are
       * sane - any value can be set.
       *
       * @param[in] aAcc        Value to set the access mask to.
       */
      void SetAccess(const uint32_t aAcc) { access=aAcc; }
      
    public:
      uint16_t access;          ///< Access value. See MaCI::MaCICtrl::EAccess.
      uint16_t value;           ///< (Reserved)
    };
  }
}

#endif
